// i.r(t), i.d(t, { default: () => SchedulerModule })

import Module from "./Module"
import CircularBuffer from "../three/CircularBuffer"
class TaskType {
  name: any
  taskDurations: CircularBuffer
  medianDuration: number
  constructor(e) {
    ;(this.name = e), (this.taskDurations = new CircularBuffer(100)), (this.medianDuration = 1)
  }
  get typicalStepDuration() {
    return this.medianDuration
  }
  addTaskDuration(e) {
    this.taskDurations.push(e), this.taskDurations.index % 10 == 0 && (this.medianDuration = this.taskDurations.median())
  }
}
var n = {
  DONE: "done",
  STARTED: "started",
  WAITING: "waiting"
}

class Scheduler {
  tasks: any[]
  types: {}
  constructor() {
    ;(this.tasks = []), (this.types = {})
  }
  getType(e) {
    return this.types[e] || (this.types[e] = new TaskType(e)), this.types[e]
  }
  addTask(e) {
    this.tasks.push(e)
  }
  update(e) {
    ;(this.tasks = this.tasks.filter(e => e.status !== n.DONE)), this.tasks.sort((e, t) => t.urgency - e.urgency)
    let t = 0
    e: for (const i of this.tasks)
      for (; i.status !== n.DONE; ) {
        const n = performance.now(),
          s = t + i.type.typicalStepDuration > e,
          r = n >= i.deadline
        if (s && !r) break e
        const o = n
        i.run()
        const a = performance.now() - o
        i.type.addTaskDuration(a), (t += a)
      }
  }
}
import { ScheduleTaskCommand, ScheduleProcessCommand } from "../command/task.command"

class Task {
  type: any
  func: any
  deadline: any
  steps: any
  status: any
  promise: Promise<unknown>
  onSuccess: (value: unknown) => void
  onError: (reason?: any) => void
  constructor(e, t, i, s) {
    ;(this.type = e),
      (this.func = t),
      (this.deadline = i),
      (this.steps = s),
      (this.status = n.WAITING),
      (this.promise = new Promise((e, t) => {
        ;(this.onSuccess = e), (this.onError = t)
      }))
  }
  run() {
    let e
    this.status = n.STARTED
    try {
      e = this.func.next()
    } catch (e) {
      return (this.status = n.DONE), void this.onError(e)
    }
    ;(this.steps -= 1), e.done && ((this.status = n.DONE), this.onSuccess(e.value))
  }
  get estimatedDuration() {
    return this.steps * this.type.typicalStepDuration
  }
  get urgency() {
    const e = this.deadline - performance.now()
    return e <= 0 ? 1e6 - e : this.estimatedDuration / e / e
  }
}
export default class SchedulerModule extends Module {
  scheduler: Scheduler
  engine: any
  constructor() {
    super(...arguments), (this.name = "scheduler-module"), (this.scheduler = new Scheduler())
  }
  async init(e, t) {
    ;(this.engine = t),
      this.engine.commandBinder.addBinding(ScheduleTaskCommand, async e => {
        const t = new Task(this.scheduler.getType(e.type), e.func, performance.now() + e.maxDelay, e.steps)
        return this.scheduler.addTask(t), t
      }),
      this.engine.commandBinder.addBinding(ScheduleProcessCommand, async e => {
        const t = new Task(this.scheduler.getType(e.type), e.func, performance.now() + e.maxDelay, e.steps)
        return this.scheduler.addTask(t), t
      })
  }
  onUpdate(e) {
    this.scheduler.update(4)
  }
}
